import { Dimension, DimensionLocation, Structure, system, Vector3, world } from "@minecraft/server";
import { DynamicJson } from "../dynamic_data/dynamic_json";
import { GAME_API } from "../instances/gameapi_unique_id";
import { MapChunkInfo } from "./map_chunk_info";
import { DebugLog, Logger } from "../utils/debug_log";
import { deferredRegister, DeferredValue } from "../deferred_register/deferred_register";
import { MinecraftDimensionTypes, MinecraftEntityTypes } from "@minecraft/vanilla-data";

export const MAP_MANAGER_LARGE_MAP_INFO_ID = "large_map_info";

type StructureType = string | Structure;

export const STRUCTURE_MAX_SIZE_VOLUME: Vector3 = { x: 64, y: 384, z: 64 };

export class MapManager {
    large_map_info: DeferredValue<DynamicJson<Record<string, MapChunkInfo>>>;
    private logger: Logger;
    private dimension?: DeferredValue<Dimension>;
    constructor(dimensionId: string) {
        // world.afterEvents.worldLoad.subscribe(() => {
        //     this.dimension = world.getDimension(dimensionId);
        // });
        // const key = GAME_API.withName(`${MAP_MANAGER_LARGE_MAP_INFO_ID}-${this.dimension?.id}`);
        this.dimension = deferredRegister.register(() => world.getDimension(dimensionId));
        const key = GAME_API.withName(
            `${MAP_MANAGER_LARGE_MAP_INFO_ID}-${this.dimension.value?.id}`
        );
        this.large_map_info = deferredRegister.register(() => new DynamicJson(world, key, {}));
        this.logger = new Logger(`${MapManager.name}:${this.dimension.value?.id}`);
    }

    private placeStrucutre(id: string, loc: Vector3) {
        if (!this.dimension?.value) return;
        world.structureManager.place(this.buildKey(id), this.dimension.value, loc);
    }

    private saveStrucuture(id: string, loc: Vector3, volume: Vector3) {
        if (world.structureManager.get(id) != undefined) this.deleteStructure(id);
        if (!this.dimension?.value) {
            return this.logger.error("维度未定义");
        }
        DebugLog.log(id);
        DebugLog.log(`${this.dimension?.value.id}`);
        const key = this.buildKey(id);
        if (world.structureManager.get(key) != undefined) {
            world.structureManager.delete(key);
        }
        world.structureManager.createFromWorld(key, this.dimension.value, loc, {
            x: loc.x + volume.x - 1,
            y: loc.y + volume.y - 1,
            z: loc.z + volume.z - 1,
        });
    }

    private *saveGenerator(id: string, loc: Vector3, volume: Vector3) {
        //剩余部分长宽
        let modX = volume.x % STRUCTURE_MAX_SIZE_VOLUME.x,
            modZ = volume.z % STRUCTURE_MAX_SIZE_VOLUME.z;

        let timesX = Math.floor((volume.x - modX) / STRUCTURE_MAX_SIZE_VOLUME.x),
            timesZ = Math.floor((volume.z - modZ) / STRUCTURE_MAX_SIZE_VOLUME.z);

        let extraPartX = modX == 0 ? 0 : 1,
            extraPartZ = modZ == 0 ? 0 : 1;

        DebugLog.log(`modX:${modX}`);
        DebugLog.log(`modZ:${modZ}`);

        //处理每个部分
        for (let i = 0; i < timesX + extraPartX; i++) {
            for (let j = 0; j < timesZ + extraPartZ; j++) {
                const chunkId = this.getChunkId(id, i, j);
                this.saveStrucuture(
                    chunkId,
                    {
                        y: loc.y,
                        x: loc.x + i * STRUCTURE_MAX_SIZE_VOLUME.x,
                        z: loc.z + j * STRUCTURE_MAX_SIZE_VOLUME.z,
                    },
                    {
                        y: volume.y,
                        x: i >= timesX ? modX : STRUCTURE_MAX_SIZE_VOLUME.x,
                        z: j >= timesZ ? modZ : STRUCTURE_MAX_SIZE_VOLUME.z,
                    }
                );
                DebugLog.log(`保存了块:${chunkId}`);
                yield;
            }
        }
        this.large_map_info.value?.update((obj) => {
            let result = obj;
            result[id] = new MapChunkInfo(
                timesX + extraPartX,
                timesZ + extraPartZ,
                volume.x,
                volume.z
            );
            return result;
        });
    }

    public save(id: string, loc: Vector3, volume: Vector3) {
        system.runJob(this.saveGenerator(id, loc, volume));
    }

    private *placeGenerator(id: string, loc: Vector3) {
        const maps = this.large_map_info.value?.get();
        DebugLog.log(JSON.stringify("mapinfo:" + maps));
        const mapInfo = maps?.[id];
        if (!mapInfo) {
            return this.logger.error("地图信息读取失败");
        }
        this.killEntitiesInArea(loc, mapInfo);
        if (mapInfo != undefined) {
            for (let i = 0; i < mapInfo.size_x; i++) {
                for (let j = 0; j < mapInfo.size_z; j++) {
                    const chunkId = this.getChunkId(id, i, j);
                    this.placeStrucutre(chunkId, {
                        y: loc.y,
                        x: loc.x + i * STRUCTURE_MAX_SIZE_VOLUME.x,
                        z: loc.z + j * STRUCTURE_MAX_SIZE_VOLUME.z,
                    });
                    DebugLog.log(`放置了块:${chunkId}`);
                    yield;
                }
            }
        }
    }

    public place(id: string, loc: Vector3) {
        system.runJob(this.placeGenerator(id, loc));
    }

    private getChunkId(id: string, x: number, y: number) {
        return `${id}_${x}_${y}`;
    }

    private buildKey(id: string) {
        return `game-api:${id}`;
    }

    private deleteStructure(id: StructureType) {
        world.structureManager.delete(id);
    }

    public delete(id: string) {
        let largeMapInfoObj = this.large_map_info.value?.get();
        const mapChunkInfo = largeMapInfoObj?.[id];
        if (!largeMapInfoObj || !mapChunkInfo) return;

        for (let i = 0; i < mapChunkInfo.size_x; i++) {
            for (let j = 0; j < mapChunkInfo.size_z; j++) {
                this.deleteStructure(this.getChunkId(id, i, j));
            }
        }

        delete largeMapInfoObj[id];
        this.large_map_info.value?.set(largeMapInfoObj);
    }

    private killEntitiesInArea(loc: Vector3, mapInfo: MapChunkInfo) {
        if (!this.dimension?.value) return;
        //后面把高度加上
        DebugLog.log(`${JSON.stringify(loc)},${mapInfo.real_size_x}-${mapInfo.real_size_z}`);
        const entities = this.dimension.value.getEntities({
            excludeTypes: [MinecraftEntityTypes.Player],
            location: loc,
            volume: { x: mapInfo.real_size_x, y: 384, z: mapInfo.real_size_z },
        });
        // DebugLog.log("清除" + entities.length.toString() + "个生物");
        entities.forEach((e) => {
            DebugLog.log(`移除了${e.typeId}`);
            e.remove();
        });
    }
}

export const overworldMapManager = new MapManager(MinecraftDimensionTypes.Overworld);
export const netherMapManager = new MapManager(MinecraftDimensionTypes.Nether);
export const theEndMapManager = new MapManager(MinecraftDimensionTypes.TheEnd);
